#ifndef GRAPHPREDICATES_HPP_INCLUDED
#define GRAPHPREDICATES_HPP_INCLUDED

#include <boost/graph/graph_traits.hpp>
#include "boost/tuple/tuple.hpp"
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/subgraph.hpp>
#include "boost/tuple/tuple_io.hpp"
#include <string>
#include <vector>
#include "GraphHandler.hpp"


struct HexCharStruct
{
  unsigned char c;
  HexCharStruct(char _c) : c(_c) { }
};

inline std::ostream& operator<<(std::ostream& o, const HexCharStruct& hs)
{
  return (o << std::setw(2) << std::setfill('0') << std::hex << (int)hs.c);
}

inline HexCharStruct hex(char _c)
{
  return HexCharStruct(_c);
}


// Handles the writing of the properties of each vertex when exporting to dot format(Graphviz)

class Vertex_writer
{
public:

    Vertex_writer(GraphHandler::Graph _graph) : graph(_graph)  {}
    Vertex_writer(GraphHandler::Graph _graph, std::vector<const GraphHandler::Graph*> _pOverlay) : graph(_graph), m_pOverlaygraph(_pOverlay)  {}

    void operator()(std::ostream& out, const GraphHandler::Vertex& v) const;

private:
    GraphHandler::Graph graph;
    const std::vector<const GraphHandler::Graph*> m_pOverlaygraph;
};

// Handles the writing of the properties of each edge when exporting to dot format(Graphviz)
template <typename GeneralGraph>
class Edge_writer
{
public:
    Edge_writer(GeneralGraph _graph, std::vector<const GeneralGraph*> _pOverlay) : graph(_graph), m_pOverlaygraph(_pOverlay) {}


    template <typename Edge>
    void operator()(std::ostream& out, const Edge& e) const
    {
        unsigned char R = 0, G = 0, B = 0, A = 255;
        typename boost::graph_traits<GeneralGraph>::edge_iterator firstE, lastE;
        int iS = boost::get(boost::vertex_name, graph, source(e, graph));
        int iT = boost::get(boost::vertex_name, graph, target(e, graph));
        bool bMatch0 = false;
        bool bMatch1 = false;
        bool bMatch2 = false;




        /*
    // Yellow means it is predicted and in the partial
    // Red means its only in the partial and not in the predicted

          */

        for(int j=0; j < m_pOverlaygraph.size(); j++)
        {
            for (boost::tie(firstE, lastE) = boost::edges(*m_pOverlaygraph[j]); firstE != lastE; ++firstE)
            {
                if( (boost::get(boost::vertex_name, *m_pOverlaygraph[j], source(*firstE, *m_pOverlaygraph[j])) == iS &&
                     boost::get(boost::vertex_name, *m_pOverlaygraph[j], target(*firstE, *m_pOverlaygraph[j])) == iT) ||
                        (boost::get(boost::vertex_name, *m_pOverlaygraph[j], source(*firstE, *m_pOverlaygraph[j])) == iT &&
                         boost::get(boost::vertex_name, *m_pOverlaygraph[j], target(*firstE, *m_pOverlaygraph[j])) == iS)  )
                {
                    if( j == 0 ) { R = 255; bMatch0 = true; }
                    else if( j == 1 ) {  G = 0; B = 0;  bMatch1 = true;  }
                    else if ( j==2 ) {bMatch2 = true;  }
                }

            }
        }

        /*
    kOverlay.push_back(&GraphHandler::Instance().m_kPartialG);
    kOverlay.push_back(&G);
    kOverlay.push_back(GraphHandler::Instance().m_pEGraph);
          */

        if(m_pOverlaygraph.size() == 3)
        {
        // Green means its predicted and in the actual graph but not observed yet
        if(bMatch1 && bMatch2 && !bMatch0)
        { R = 0; G = 255; B = 0; }

        // Red means its predicted but not in the actual graph
        if(bMatch1 && !bMatch2 && !bMatch0)
        { R = 255; G = 0; B = 0; }

        // Yellow means its in the partial graph but not in the hypothesis
        if(bMatch0 && !bMatch1)
        { R = 255; G = 255; B = 0; }

        // Blue means its in the hypothesis, observed
        if(bMatch0 && bMatch2 && bMatch1)
        { R = 0; G = 0; B = 255; }

        // Black is normal color
        if(!bMatch0 && !bMatch1 && !bMatch2)
        { R = 0; G = 0; B = 0; }
        }

        out << "[color=\"#" << hex(R) << hex(G) << hex(B) << hex(A) << "\"]";
    }


private:
    GeneralGraph graph;
    const std::vector<const GeneralGraph*> m_pOverlaygraph;
};

/*
template <typename GeneralGraph>
class Vertex_writer
{
public:

    Vertex_writer(GeneralGraph _graph) : graph(_graph) {}

    template <typename Vertex>
    void operator()(std::ostream& out, const Vertex& v) const {
        out << "[label=\"" << graph[v].sName << "\"]";
    }
private:
    GeneralGraph graph;
};
*/



// Handles the writing of the properties of each vertex when exporting to dot format(Graphviz)
template <typename GeneralGraph>
class MetaVertex_writer
{
public:

    MetaVertex_writer(GeneralGraph _graph) : graph(_graph) {}

    template <typename Vertex>
    void operator()(std::ostream& out, const Vertex& v) const {
      //out << "[label=\"" << graph[v].sName << "\"]," <<
      //"[i=\"" << graph[v].iFreq << "\"]";
      unsigned char R = 255, G = 0, B = 0, A = (unsigned char)((600.0 / ((double)graph[v].iFreq)) * 255.0);
      R=A;
      //graph[v].iFreq
        out << "[label=\"" << graph[v].sName << "\"," <<
        "color=\"#" << hex(R) << hex(G) << hex(B) << hex(A) << "\"," <<
        "style=\"filled\"]";

        //style=filled
        out << std::dec;
      //color
      //"#%2x%2x%2x%2x"
    }
private:
    GeneralGraph graph;
};

// Handles the writing of the properties of each edge when exporting to dot format(Graphviz)
template <typename GeneralGraph>
class MetaEdge_writer
{
public:
    MetaEdge_writer(GeneralGraph _graph) : graph(_graph) {}

    template <typename Edge>
    void operator()(std::ostream& out, const Edge& e) const {
      out << "[i=\"" << graph[e].iFreq << "\"]";
    }


private:
    GeneralGraph graph;
};

// Used for filtering out those vectors which are below a certain frequency (in the meta-graph).
// Predicate function class
template <typename GeneralGraph>
struct vertex_freq_filter
{
    GeneralGraph graph;
    int iCutOff;

    vertex_freq_filter() { }
    vertex_freq_filter(GeneralGraph _graph, int _cutoff) : graph(_graph), iCutOff(_cutoff) { }

    template <typename Vertex>
    bool operator()(const Vertex& v) const
    {
        return graph[v].iFreq > iCutOff;
    }


};

// Used for filtering out those edges which are below a certain frequency (in the meta-graph).
// Predicate function class
template <typename GeneralGraph>
struct edge_freq_filter
{
    edge_freq_filter() { }
    edge_freq_filter(GeneralGraph _graph, int _cutoff) : graph(_graph), iCutOff(_cutoff) { }

    template <typename Edge>
    bool operator()(const Edge& v) const
    {
        return graph[v].iFreq > iCutOff;
    }

    GeneralGraph graph;
    int iCutOff;
};

#endif // GRAPHPREDICATES_HPP_INCLUDED
